import subprocess
from threading import Thread, Semaphore
from cStringIO import StringIO

class _DaemonicReader(Thread):
    """Object that reads data from given blocking file-like object \
    in separate thread.

    (Reads and call handler functions).

    """

    _pipe = _buffer = _pipeSem = None
    _finish = False

    def __init__(self, srcPipe):
        super(_DaemonicReader, self).__init__()
        self._pipe = srcPipe
        self._buffer = StringIO()
        self._pipeSem = Semaphore()
        self.setDaemon(True)
        self.start()

    def finish(self):
        self._finish = True

    def _onDataRecv(self, data):
        """Called upon new data acqire."""
        raise NotImplementedError

    def run(self):
        while not self._finish and not self._pipe.closed:
            _ch = self._pipe.read(1)
            self._pipeSem.acquire()
            self._finish = self._finish or not _ch
            self._onDataRecv(_ch)
            self._pipeSem.release()

class DaemonicPipe(_DaemonicReader):
    """Object that is supposed to implement non-blocking pipe
        on top of plocing one.
    """

    def _onDataRecv(self, data):
        _pos = self._buffer.tell()
        self._buffer.seek(0, 2)
        self._buffer.write(data)
        self._buffer.seek(_pos, 0)

    def read(self, size=None):
        self._pipeSem.acquire()
        if size is None:
            _rv = self._buffer.read()
        else:
            _rv = self._buffer.read(size)
        self._pipeSem.release()
        return _rv

    def readAll(self):
        self._finish = True
        self.join()
        self._pipeSem.acquire()
        _rv = self._buffer.read()
        if not self._pipe.closed:
            _rv += self._pipe.read()
        self._pipeSem.release()
        return _rv

class NonBlockingSubprocess(object):
    """Object that tries to emulate subprocess.Popen with non-blocking
    stdin/stdout pipes.
    """

    _popen = _command = None

    stdin = property(lambda s: s._popen.stdin)
    returncode = property(lambda s: s._popen.poll())

    def __init__(self, cmd):
        self._command = cmd
        self._popen = subprocess.Popen(
            cmd,
            stdin = subprocess.PIPE,
            stdout = subprocess.PIPE,
            stderr = subprocess.PIPE,
            close_fds = False,
            shell=True
        )
        self.stdout = DaemonicPipe(self._popen.stdout)
        self.stderr = DaemonicPipe(self._popen.stderr)

    def wait(self):
        return self._popen.wait()

    def poll(self):
        return self._popen.poll()

# vim: set sts=4 sw=4 et :
